package com.byapp.fast.modules.match.service.impl;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.byapp.fast.common.converter.MatchBetDTOConverter;
import com.byapp.fast.common.converter.MatchDetailDTOConverter;
import com.byapp.fast.common.converter.MatchMsgEventDTOConverter;
import com.byapp.fast.common.enums.DateUnitEnum;
import com.byapp.fast.common.enums.PlayIdEnum;
import com.byapp.fast.common.enums.ResultEnum;
import com.byapp.fast.common.exception.BYException;
import com.byapp.fast.common.utils.CheckUtils;
import com.byapp.fast.common.utils.DateUtils;
import com.byapp.fast.common.utils.JsonUtils;
import com.byapp.fast.datasources.annotation.DataSource;
import com.byapp.fast.modules.contract.service.ContractService;
import com.byapp.fast.modules.match.dao.MatchDao;
import com.byapp.fast.modules.match.dao.MatchRefDao;
import com.byapp.fast.modules.match.dto.*;
import com.byapp.fast.modules.match.entity.*;
import com.byapp.fast.modules.match.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StopWatch;

import java.util.*;

@Service("matchRefService")
@Slf4j
public class MatchServiceImpl extends ServiceImpl<MatchRefDao, MatchRefEntity> implements MatchService {

    @Autowired
    private MatchRefDao matchRefDao;

    @Autowired
    private MatchDao matchDao;

    @Autowired
    private MatchExtService matchExtService;

    @Autowired
    private OutComeService outComeService;

    @Autowired
    private AsianOddsService asianOddsService;

    @Autowired
    private EuropeOddsService europeOddsService;

    @Autowired
    private MatchOddsService matchOddsService;

    @Autowired
    private ContractService contractService;


    @Override
    public MatchRefEntity queryById(String matchId) {
        log.info("【查询赛事信息】查询参数：matchId={}", matchId);
        MatchRefEntity matchRefEntity = this.selectById(matchId);
        if (matchRefEntity == null) {
            log.error("【查询赛事信息】赛事信息不存在，查询参数：matchId={}", matchId);
            throw new BYException(ResultEnum.ILLEGAL_PARAM);
        }
        return matchRefEntity;
    }

    @Override
    public List<MatchDateDTO> queryMatchDateGroup() {
        log.info("【查询赛事日期分组数据】");
        return matchRefDao.queryMatchDateGroup();
    }

    @Override
    public List<MatchLeagueDTO> queryMatchLeagueGroup() {
        log.info("【查询联赛分组数据】");
        return matchRefDao.queryMatchLeagueGroup();
    }

    @Override
    public List<MatchDetailDTO> queryMatchDateList(String matchTime) {
        log.info("【查询赛事列表数据】查询参数：matchTime={}", matchTime);
        Date matchDate = DateUtils.parseDate(matchTime);

        // 赛事时间区间：matchTime所在日期10AM~隔天10AM
        Date startTime = DateUtils.add(matchDate, DateUnitEnum.HOUR, 10);
        Date endTime = DateUtils.add(startTime, DateUnitEnum.DAY, 1);

        // 获取赛事信息列表
        List<MatchRefEntity> list = this.selectList(
                new EntityWrapper<MatchRefEntity>().gt("MATCH_TIME", new Date())
                        .between("MATCH_TIME", startTime, endTime)
                        .eq("REF_STATUS", 1)
        );

        // 赛事对象转换
        return MatchDetailDTOConverter.mapToTarget(list);
    }

    @Override
    public List<MatchDetailDTO> queryMatchLeagueList(String leagueId) {
        log.info("【查询赛事列表数据】查询参数：leagueId={}", leagueId);
        // 获取赛事信息列表
        List<MatchRefEntity> list = this.selectList(
                new EntityWrapper<MatchRefEntity>().gt("MATCH_TIME", new Date())
                        .eq("LEAGUE_ID", leagueId)
                        .eq("REF_STATUS", 1)
        );
        // 赛事对象转换
        return MatchDetailDTOConverter.mapToTarget(list);
    }

    @Override
    @DataSource(name = "match")
    public MatchExtEntity queryMatchExt(String matchId) {
        log.info("【查询赛事扩展信息】查询参数：matchId={}", matchId);
        MatchExtEntity matchExtEntity = matchExtService.selectById(matchId);
        if (matchExtEntity == null) {
            log.error("【查询赛事扩展信息】赛事扩展信息不存在，查询参数：matchId={}", matchId);
            throw new BYException(ResultEnum.ILLEGAL_PARAM);
        }
        return matchExtEntity;
    }


    @Override
    @DataSource(name = "match")
    public List<MatchBetDTO> queryMatchOddsList(String matchId) {
        log.info("【查询赛事赔率数据】查询参数：matchId={}", matchId);
        // 查询赔率数据：亚盘、欧盘
        List<AsianOddsEntity> asianList = asianOddsService.selectList(
                new EntityWrapper<AsianOddsEntity>().eq("MATCH_ID", matchId)
                        .eq("COMPANY_ID", 453)
                        .eq("STATUS", 0)
        );

        List<EuropeOddsEntity> europeList = europeOddsService.selectList(
                new EntityWrapper<EuropeOddsEntity>().eq("MATCH_ID", matchId)
                        .eq("COMPANY_ID", 453)
                        .eq("STATUS", 0)
        );


        List<BaseOddsEntity> oddsList = new ArrayList<>();
        oddsList.addAll(asianList);
        oddsList.addAll(europeList);

        convertPlayId(oddsList);

        return MatchBetDTOConverter.mapToTarget(oddsList);
    }

    @Override
    @Transactional
    public void updateMatchOdds(MatchMsgEventDTO matchMsgEventDTO) {
        StopWatch stopWatch = new StopWatch("updateMatchOdds");
        stopWatch.start("更新赛事赔率/盘口");
        // 请求参数
        String matchPhase = matchMsgEventDTO.getMatchPhase();
        String play = matchMsgEventDTO.getPlay();
        String playId = mapPlayId(play, matchPhase);
        String matchId = matchMsgEventDTO.getMatchId();
        String oddsPhase = matchMsgEventDTO.getOddsPhase();
        String handicap = matchMsgEventDTO.getHandicap();
        matchMsgEventDTO.setPlayId(playId);

        Map<String, Object> map = new HashMap<>();
        map.put("MATCH_ID", matchId);
        map.put("PLAY_ID", playId);
        map.put("HANDICAP", handicap);
        map.put("ODDS_PHASE", oddsPhase);

        // 判断该赛事是否开售过(T_MATCH_REF有对应记录)
        log.info("【更新赛事赔率/盘口】查询赛事开售记录，MatchId={}", matchId);
        MatchRefEntity matchRefEntity = this.selectById(matchId);

        if (matchRefEntity == null) {
            log.error("【更新赛事赔率/盘口】赛事未开售，MatchId={}", matchId, ResultEnum.ILLEGAL_PARAM.getMsg());
            return;
        }

        MatchOddsEntity findResult = queryMatchOdds(map);

        // 数据转换
        MatchOddsEntity matchOddsEntity = new MatchOddsEntity();
        boolean isExists = false; // 标识是否存在记录
        if (findResult != null) {
            // 若已经存在记录，则将数据复制到目标对象中
            BeanUtils.copyProperties(findResult, matchOddsEntity);
            isExists = true;
        }

        // 执行插入或更新操作
        MatchMsgEventDTOConverter.create(matchMsgEventDTO, matchOddsEntity);
        log.info("【更新赛事赔率/盘口】执行插入或更新操作，matchOddsEntity={}", JsonUtils.toJson(matchOddsEntity));
        boolean flag = matchOddsService.insertOrUpdate(matchOddsEntity);

        if (!flag) {
            log.error("【更新赛事赔率/盘口】执行插入或更新操作失败，matchOddsEntity={}", JsonUtils.toJson(matchOddsEntity));
            throw new BYException(ResultEnum.ILLEGAL_PARAM);
        }

        // 操作成功后调用接口：查询获取最新的数据
        log.info("【更新赛事赔率/盘口】查询最新赔率数据，map={}", JsonUtils.toJson(map));
        MatchOddsEntity newMatchOdds = queryMatchOdds(map);
        stopWatch.stop();

        if (isExists) {
            stopWatch.start("合约更新赔率接口");
            contractService.callChangeBetOdds(newMatchOdds);
            stopWatch.stop();
        } else {
            stopWatch.start("合约新增玩法接口");
            contractService.callAddPlay(newMatchOdds, matchRefEntity);
            stopWatch.stop();
        }

        log.info("【执行日志】{}", stopWatch.prettyPrint());
    }

    @Override
    public String queryOdds(String oddsId, String type) {
        String odds;
        MatchOddsEntity matchOddsEntity = matchOddsService.selectById(oddsId);
        switch (type) {
            case "3":
                odds = matchOddsEntity.getWin();
                break;
            case "1":
                odds = matchOddsEntity.getEven();
                break;
            case "0":
                odds = matchOddsEntity.getLost();
                break;
            default:
                odds = "0";
                break;
        }
        return odds;
    }

    @Override
    @DataSource(name = "match")
    public boolean isMatchStarted(String matchId) {
        log.info("【查询赛事是否开始】查询参数：matchId={}", matchId);
        MatchEntity matchEntity = matchDao.selectById(matchId);
        return matchEntity.getMatchTime().getTime() <= new Date().getTime();
    }

    @Override
    @DataSource(name = "match")
    public boolean isMatchBetEnabled(String matchId) {
        log.info("【查询赛事封盘状态】查询参数：matchId={}", matchId);
        MatchExtEntity matchExtEntity = queryMatchExt(matchId);
        return matchExtEntity.getBetStatus() == 0;
    }

    @Override
    @DataSource(name = "match")
    public boolean isPlayBetEnabled(Map<String, Object> params) {
        String betType = String.valueOf(params.get("BET_TYPE"));
        if (!StringUtils.equalsIgnoreCase(betType, "ASIAN")) {
            params.remove("HANDICAP_VALUE");
        }
        log.info("【查询玩法封盘状态】查询参数：params={}", JsonUtils.toJson(params));
        OutComeEntity outComeEntity = outComeService.selectOne(
                new EntityWrapper<OutComeEntity>().allEq(params)
        );
        if (outComeEntity == null) {
            log.error("【查询玩法封盘状态】赛事赔率信息不存在，查询参数：params={}", JsonUtils.toJson(params));
            throw new BYException(ResultEnum.ILLEGAL_PARAM);
        }

        return StringUtils.equals(outComeEntity.getBetStatus(), "0");
    }

    @Override
    @DataSource(name = "match")
    public boolean isOriginOddsAndHandicap(Map<String, Object> params) {
        log.info("【校验赔率/盘口变化】查询参数：params={}", JsonUtils.toJson(params));

        // 获取请求参数
        String play = params.get("PLAY").toString();
        String codes = params.get("CODES").toString();
        String odds = params.get("ODDS").toString();
        String handicap = params.get("HANDICAP_VALUE").toString();

        // 移除非查询参数
        params.remove("ODDS");
        params.remove("CODES");
        params.remove("PLAY");
        String originOdds = null;
        String originHandicap = "0"; // 盘口默认值为0

        // 亚盘
        if (PlayIdEnum.GLOBAL_ASIAN.getPlay().contains(play)) {
            AsianOddsEntity asianOddsEntity = asianOddsService.selectOne(
                    new EntityWrapper<AsianOddsEntity>().allEq(params)
            );
            if (asianOddsEntity == null) {
                log.error("【校验赔率/盘口变化】赛事赔率信息不存在，查询参数：params={}", JsonUtils.toJson(params));
                throw new BYException(ResultEnum.ILLEGAL_PARAM);
            }
            originHandicap = asianOddsEntity.getHandicapValue();
            switch (codes) {
                case "0":
                    originOdds = asianOddsEntity.getFixtrueLost();
                    break;
                case "3":
                    originOdds = asianOddsEntity.getFixtrueWin();
                    break;
                default:
                    break;
            }
        }
        // 欧盘
        if (PlayIdEnum.GLOBAL_EUROPE.getPlay().contains(play)) {
            params.remove("HANDICAP_VALUE");
            EuropeOddsEntity europeOddsEntity = europeOddsService.selectOne(
                    new EntityWrapper<EuropeOddsEntity>().allEq(params)
            );
            if (europeOddsEntity == null) {
                log.error("【校验赔率/盘口变化】赛事赔率信息不存在，查询参数：params={}", JsonUtils.toJson(params));
                throw new BYException(ResultEnum.ILLEGAL_PARAM);
            }
            switch (codes) {
                case "0":
                    originOdds = europeOddsEntity.getFixtrueLost();
                    break;
                case "1":
                    originOdds = europeOddsEntity.getFixtrueDraw();
                    break;
                case "3":
                    originOdds = europeOddsEntity.getFixtrueWin();
                    break;
                default:
                    break;
            }
        }
        return !(StringUtils.equals(odds, originOdds) && StringUtils.equals(handicap, originHandicap));
    }

    @Override
    public MatchOddsEntity queryMatchOdds(Map<String, Object> map) {
        String[] array = new String[]{"MATCH_ID", "PLAY_ID", "HANDICAP", "ODDS_PHASE"};
        boolean contains = CheckUtils.isALLContains(map, array);
        // 参数必要性检测
        log.info("【参数必要性检测】，map={}", JsonUtils.toJson(map));
        if (!contains || CheckUtils.isAnyEmpty(map)) {
            log.error("【参数必要性检测失败】，map={}", JsonUtils.toJson(map));
            return null;
        }

        // 查询是否存在赔率数据
        log.info("【查询是否存在赔率数据】，map={}", JsonUtils.toJson(map));
        return matchOddsService.selectOne(
                new EntityWrapper<MatchOddsEntity>()
                        .allEq(map)
        );
    }

    @Override
    public MatchOddsEntity queryMatchOdds(String oddsId) {
        return matchOddsService.selectById(oddsId);
    }

    /**
     * 设置PlayId
     */
    private static void convertPlayId(List<? extends BaseOddsEntity> list) {
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        list.forEach(entity -> {
            String matchPhase = entity.getMatchPhase();
            String playId = null;
            // 亚盘
            if (entity instanceof AsianOddsEntity) {
                playId = mapPlayId("ASIAN", matchPhase);
            }
            // 欧盘
            if (entity instanceof EuropeOddsEntity) {
                playId = mapPlayId("EUROPE", matchPhase);
            }
            entity.setPlayId(playId);
        });
    }

    private static String mapPlayId(String play, String matchPhase) {
        PlayIdEnum[] playIdEnums = PlayIdEnum.values();
        String origin = String.format("%s-%s", play, matchPhase);
        for (PlayIdEnum playIdEnum : playIdEnums) {
            if (StringUtils.equalsIgnoreCase(playIdEnum.getPlay(), origin)) {
                return playIdEnum.getCode().toString();
            }
        }
        return null;
    }


}
